package net.pimpas.web.jsf.components.ajax.context;

import java.io.IOException;
import java.io.Writer;
import java.util.Calendar;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import net.pimpas.web.jsf.renderers.AjaxRendererBase;
import net.pimpas.web.jsf.util.AjaxUtils;
import net.pimpas.xml.serviceprovider.XmlServiceProvider;
import net.pimpas.xml.serviceprovider.XmlServiceProviderFactoryImpl;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

/**
 * @author pc
 *
 */
public class ResponseWriterAjaxWrapper extends ResponseWriter {
	
	private ResponseWriter responseWriter;
	private XmlServiceProvider xmlProvider;
	private Element elements;
	private Element element;
	private Element current;
	private Document document;
	
	public ResponseWriterAjaxWrapper(ResponseWriter writer) {
		this.setResponseWriter(writer);
		this.initResponse( );
	}

	protected void initResponse() {
		this.setXmlProvider(XmlServiceProviderFactoryImpl.getXmlServiceProvider());
		// TODO using direct dom4j, no time to implement in pimpas-xml
        this.setDocument(DocumentHelper.createDocument());
        Element root = this.getDocument().addElement( "ajax" );
        root.addAttribute("type", "ajax");
        Calendar c = Calendar.getInstance();
        root.addAttribute("created", c.get(Calendar.HOUR_OF_DAY)+
        		":"+c.get(Calendar.MINUTE)+":"+c.get(Calendar.SECOND)+":"+c.get(Calendar.MILLISECOND));

		// myfaces HAX
		// jsf_sequence, change it!
		//AjaxUtils.renderJsfSequence( this, root  );
		
		// myfacex HAX
		// render form view state
		AjaxUtils.renderViewState( this, root );
		
        this.setElements(root.addElement( "elements" ));
        this.setCurrent(null);
	}
	
	public String getResponseText( ) {
		return this.getDocument().asXML();
	}

	public Writer append(char arg0) throws IOException {
		this.write(arg0);
		return this;
	}

	public Writer append(CharSequence arg0, int arg1, int arg2) throws IOException {
		this.write(arg0.subSequence(arg1, arg2).toString());
		return this;
	}

	public Writer append(CharSequence arg0) throws IOException {
		this.write(arg0.toString());
		return this;
	}

	public ResponseWriter cloneWithWriter(Writer writer) {
		return null;
	}

	public void close() throws IOException {
		// TODO i think, component cannot close the response writer for ajax response
		//responseWriter.close();
	}

	@SuppressWarnings("unchecked")
	public void endDocument() throws IOException {
	}

	public void endElement(String name) throws IOException {
		if(this.getCurrent() == null) return;
		if(this.getCurrent().getText().length() > 0) {
			String st = this.getCurrent().getText();
			this.getCurrent().setText("");
			this.getCurrent().addCDATA(st);
		}
		
		Element parent = this.getCurrent().getParent();
		this.setCurrent(parent);
	}

	public boolean equals(Object arg0) {
		return responseWriter.equals(arg0);
	}

	public void flush() throws IOException {
		this.getResponseWriter().flush();
	}

	public String getCharacterEncoding() {
		return this.getResponseWriter().getCharacterEncoding();
	}

	public String getContentType() {
		return this.getResponseWriter().getContentType();
	}

	public int hashCode() {
		return responseWriter.hashCode();
	}

	public void startDocument() throws IOException {
		
	}
	
	public void startForComponent(FacesContext context, UIComponent component, AjaxRendererBase renderer) {
		Element el = this.getElements().addElement("element");
		this.setElement(el);
		el.addElement("id").setText(component.getClientId(context));
		el.addElement("handler").setText(renderer.getJavascriptHandler());
		this.setCurrent(el);
	}

	public void startElement(String name, UIComponent component) throws IOException {
		Element el = this.getCurrent().addElement(name);
		this.setCurrent(el);
	}

	public String toString() {
		return responseWriter.toString();
	}

	public void write(char[] arg0, int arg1, int arg2) throws IOException {
		if(arg0 != null && arg1 >=0 && arg2 >=0 && arg1 >= arg0.length && arg2 <= arg0.length)
			this.write(new String(arg0, arg1, arg2));
		else
			throw new ArrayIndexOutOfBoundsException();
	}

	public void write(char[] arg0) throws IOException {
		this.write(new String(arg0));
	}

	public void write(int arg0) throws IOException {
		StringBuffer buff = new StringBuffer(this.getCurrent().getText());
		buff.append((char) arg0);
		this.getCurrent().addText(new String(buff));
	}

	public void write(String arg0, int arg1, int arg2) throws IOException {
		if(arg0 == null || arg1 >= arg0.length() || arg1 < 0 || arg2 < 0 || arg2 >= arg0.length()) return; 
		
		this.write(arg0.substring(arg1, arg2));
	}

	public void write(String arg0) throws IOException {
		if(this.getCurrent() == null) return;
		this.getCurrent().addText(arg0);
	}

	public void writeAttribute(String name, Object value, String property) throws IOException {
		// TODO access property?!
		this.getCurrent().addAttribute(name, ""+value);
	}

	public void writeComment(Object comment) throws IOException {
	}

	public void writeText(char[] text, int off, int len) throws IOException {
		StringBuilder sb = new StringBuilder();
		for(int i=0;i<len;i++)
			sb.append(text[off++]);
		
		this.write(new String(sb));
	}

	public void writeText(Object text, String property) throws IOException {
		// TODO ignore property
		this.write((String) text);
	}

	public void writeURIAttribute(String name, Object value, String property) throws IOException {
		//TODO fix
		this.writeAttribute(name, value, property);
	}

	public ResponseWriter getResponseWriter() {
		return responseWriter;
	}

	public void setResponseWriter(ResponseWriter responseWriter) {
		this.responseWriter = responseWriter;
	}

	public XmlServiceProvider getXmlProvider() {
		return xmlProvider;
	}

	public void setXmlProvider(XmlServiceProvider xmlProvider) {
		this.xmlProvider = xmlProvider;
	}

	public Element getElements() {
		return elements;
	}

	public void setElements(Element elements) {
		this.elements = elements;
	}

	public Element getCurrent() {
		return current;
	}

	public void setCurrent(Element current) {
		this.current = current;
	}

	public Document getDocument() {
		return document;
	}

	public void setDocument(Document document) {
		this.document = document;
	}

	public Element getElement() {
		return element;
	}

	public void setElement(Element element) {
		this.element = element;
	}
}